home *** CD-ROM | disk | FTP | other *** search
- Path: xanth!cs.odu.edu!Amiga-Request
- From: Amiga-Request@cs.odu.edu (Amiga Sources/Binaries Moderator)
- Newsgroups: comp.sources.amiga
- Subject: v90i042: BBSindex 1.0 - file database utility for BBS-PC!, Part02/03
- Message-ID: <11249@xanth.cs.odu.edu>
- Date: 2 Feb 90 19:56:06 GMT
- Sender: tadguy@cs.odu.edu
- Reply-To: Eddy Carroll <ECARROLL%vax1.tcd.ie@CUNYVM.CUNY.EDU>
- Lines: 2326
- Approved: tadguy@cs.odu.edu (Tad Guy)
- X-Mail-Submissions-To: Amiga@cs.odu.edu
-
- Submitted-by: Eddy Carroll <ECARROLL%vax1.tcd.ie@CUNYVM.CUNY.EDU>
- Posting-number: Volume 90, Issue 042
- Archive-name: util/bbsindex-1.0/part02
-
- #!/bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of archive 2 (of 3)."
- # Contents: src/bbsindex.c src/command.c src/format.c
- # Wrapped by tadguy@xanth on Fri Feb 2 14:54:38 1990
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f 'src/bbsindex.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'src/bbsindex.c'\"
- else
- echo shar: Extracting \"'src/bbsindex.c'\" \(13333 characters\)
- sed "s/^X//" >'src/bbsindex.c' <<'END_OF_FILE'
- X/*
- X * BBSindex (C) Copyright Eddy Carroll, August 1989
- X *
- X * This is a utility which will scan the file catalogue of BBS-PC! and
- X * build lists of the files in various sections and directories.
- X *
- X * It is designed to be run automatically on a regular basis in the
- X * background. It reads in instructions from an input file, and
- X * produces output files based on these instructions.
- X *
- X * Usage: BBSindex [options] [scriptfile] ..
- X *
- X * With no parameters, a short help message is displayed.
- X *
- X * The possible options are:
- X *
- X * -c <file> This specifies the configuration file (CFGINFO.DAT)
- X * -d <file> This specifies the input file (default UDHEAD.DAT)
- X * -f "string" This specifies the format string for the output
- X * -h Displays the help message
- X * -n Stops AmigaDOS putting up requesters
- X * -t Trace mode - display each line before execution
- X *
- X * scriptfile is a file containing commands to control what is output.
- X * See the documentation for more info. Note that options and script files
- X * may be freely interspersed.
- X *
- X */
- X
- X#define MAIN
- X
- X#ifndef LATTICE_50
- X#include "system.h"
- X#endif
- X
- X#include "bbsindex.h"
- X
- X/*
- X * Miscellaneous static global variables
- X */
- X
- Xstatic struct IntuitionBase *IntuitionBase;
- Xstatic BPTR infile, lock;
- Xstatic struct Remember *memkey;
- Xstatic char buffer[BUFSIZE];
- Xstatic int bufpos = 0;
- Xstatic APTR requeststat; /* Ptr to window for AmigaDos requesters */
- X
- X/*
- X * print()
- X * -------
- X * Prints a string to standard output
- X */
- Xvoid print(s)
- Xchar *s;
- X{
- X Write(errorfile,s,strlen(s));
- X}
- X
- X/*
- X * Cleanup()
- X * ---------
- X * Releases all program resources, and exits. Note that because of
- X * the way it checks to see if a resource is allocated, you need to
- X * set pointers to NULL if you release the object associated with
- X * them. For example, when you close a file, remember to set the
- X * file handle to NULL immediately afterwards, otherwise Cleanup()
- X * will try to close it again.
- X */
- Xvoid Cleanup(code)
- Xint code;
- X{
- X static ctrlc;
- X struct Process *me = (struct Process *)FindTask(0L);
- X int i;
- X
- X if (!ctrlc) {
- X ctrlc = 1;
- X me->pr_WindowPtr = requeststat;
- X if (script)
- X FreeMem(script, scriptsize);
- X for (i = 0; i < nummacros; i++)
- X FreeMem(macros[i], MACROSIZE + macros[i]->size);
- X for (i = 0; i < nestlevel; i++)
- X FreeMem(params[i], PARAMSIZE + params[i]->size);
- X if (memkey)
- X FreeRemember(&memkey, TRUE);
- X if (IntuitionBase)
- X CloseLibrary(IntuitionBase);
- X if (lock)
- X UnLock(lock);
- X if (dirlock)
- X UnLock(dirlock);
- X if (infile)
- X Close(infile);
- X if (outfile != Output())
- X Close(outfile);
- X if (errorfile)
- X Close(errorfile);
- X exit(code);
- X }
- X}
- X
- X/*
- X * cxovf()
- X * -------
- X * The standard lattice "stack abort" code. This is called when the
- X * stack overflows (having first reset the stack pointer to something
- X * safe of course).
- X */
- Xvoid cxovf()
- X{
- X print("\nBBSindex: Stack overflow! Use the STACK command "
- X "to increase stack size\n");
- X Cleanup(32);
- X}
- X
- X/*
- X * chkabort()
- X * ----------
- X * My own version of chkabort(), called by the Lattice Library
- X * functions periodically to check for CTRL-C.
- X *
- X * This version cleans up first (otherwise, after CTRL-C'ing a few times
- X * while processing a 220K UDHEAD.DAT file, you find you don't have any
- X * memory left!).
- X */
- Xvoid chkabort()
- X{
- X if (SetSignal(0,0) & SIGBREAKF_CTRL_C) {
- X SetSignal(0,0xffffffff);
- X print("^C\n");
- X Cleanup(5);
- X }
- X}
- X
- X
- X/*
- X * SafeAllocMem()
- X * --------------
- X * A revised AllocMem() that checks for out of memory.
- X */
- Xvoid *SafeAllocMem(size)
- Xint size;
- X{
- X void *ptr = AllocMem(size, 0);
- X if (!ptr) {
- X print("BBSindex: Out of memory!\n");
- X Cleanup(20);
- X }
- X return (ptr);
- X}
- X
- X
- X/*
- X * mymalloc()
- X * ----------
- X * My own private malloc. No memory limits, and no library overhead.
- X * Easy to replace the call to AllocRemember with one to malloc for
- X * portability. To try and keep down memory fragmentation, requests
- X * for memory less than 200 bytes are allocated from a larger block
- X * which is allocated "on the fly".
- X */
- Xvoid *mymalloc(size)
- Xint size;
- X{
- X static void *bigblock; /* Current large memory block */
- X static int curpos = FRAGBLOCK; /* Current position in block */
- X void *ptr;
- X
- X size = (size + 7) & 0xfffffff8; /* Round up to 8 block boundary */
- X if (size < FRAGTHRESH) {
- X /*
- X * If we want a small chunk, allocate it from our intermediate
- X * block.
- X */
- X if ((size + curpos) > FRAGBLOCK) {
- X bigblock = mymalloc(FRAGBLOCK);
- X curpos = 0;
- X }
- X ptr = (char *)bigblock + curpos;
- X curpos += size;
- X } else {
- X /*
- X * Otherwise, just allocate memory as normal.
- X */
- X ptr = AllocRemember(&memkey, size, 0);
- X if (!ptr) {
- X print("BBSindex: Out of memory!\n");
- X Cleanup(20);
- X }
- X }
- X return (ptr);
- X}
- X
- X
- X/*
- X * dumpdata()
- X * ----------
- X * Outputs raw data to a file, checking for write errors.
- X */
- Xvoid dumpdata(buf,len)
- Xchar *buf;
- Xint len;
- X{
- X if (Write(outfile, buf, len) != len) {
- X print("BBSindex: Error writing output file (disk full?)\n");
- X Cleanup(10);
- X }
- X}
- X
- X/*
- X * putstring()
- X * -----------
- X * This function outputs a string to the current output file. If the
- X * file is a tty, the string is output immediately, else it is
- X * buffered up and output when it is big enough.
- X */
- X
- Xvoid putstring(s)
- Xchar *s;
- X{
- X int len = strlen(s);
- X
- X if (toscreen) {
- X dumpdata(s, len);
- X } else {
- X if ( (bufpos + len) > BUFSIZE) {
- X dumpdata(buffer, bufpos);
- X bufpos = 0;
- X }
- X strcpy(buffer+bufpos, s);
- X bufpos += len;
- X }
- X}
- X
- X/*
- X * flushout()
- X * ----------
- X * Flushes output to disk, before closing a file.
- X */
- X
- Xvoid flushout()
- X{
- X if (bufpos > 0) {
- X dumpdata(buffer, bufpos);
- X }
- X bufpos = 0;
- X}
- X
- X/*
- X * openfile()
- X * ----------
- X * Opens a specified file for reading, and returns the length
- X * of the file. If an error occurs, aborts automatically. The
- X * caller has responsibility for calling Close(infile) when finished
- X * with the file.
- X */
- Xlong openfile(filename)
- Xchar *filename;
- X{
- X if ((lock = Lock(filename, ACCESS_READ)) == NULL) {
- X print3("BBSindex: Can't access file ",filename,"\n");
- X Cleanup(5);
- X }
- X
- X if (!Examine(lock, fib)) {
- X print3("BBSindex: Can't get info for file ",filename,"\n");
- X Cleanup(5);
- X }
- X UnLock(lock); lock = NULL;
- X
- X if ((infile = Open(filename, MODE_OLDFILE)) == NULL) {
- X print3("BBSindex: Can't open file ",filename," for input\n");
- X Cleanup(5);
- X }
- X return (fib->fib_Size);
- X}
- X
- X/*
- X * readdatabase()
- X * --------------
- X * This function reads in the entire file database into memory,
- X * allocating memory as required. It sets up an array of pointers to
- X * the records in the database, which is used by qsort() among other
- X * things.
- X *
- X * Note that the memory needed is allocated in a number of small
- X * chunks each BLOCKSIZE * UDSIZE bytes in size (~16K by default).
- X * This means there doesn't need to be a contigous block of memory
- X * big enough to hold the whole file database available - it is
- X * split into smaller chunks instead.
- X */
- X
- Xvoid readdatabase(filename)
- Xchar *filename;
- X{
- X long size;
- X long bsize;
- X long i,j;
- X UDHEAD *block; /* Pointer to block of headers */
- X UDHEAD **p;
- X
- X readfiles = TRUE;
- X
- X size = openfile(filename);
- X if ((size % UDSIZE) || (size < (2 * UDSIZE))) {
- X print3("BBSindex: ",filename,
- X " isn't a valid BBS-PC! file header file!\n");
- X Cleanup(5);
- X }
- X
- X /*
- X * File successfully opened, now read in file data.
- X * Skip past unneeded data at start of BBS-PC! file
- X */
- X Seek(infile, UDSIZE * 2, OFFSET_BEGINNING);
- X size = size - (2 * UDSIZE);
- X numrecs = size/UDSIZE;
- X ptrblock = mymalloc(numrecs * sizeof(UDHEAD *));
- X p = ptrblock;
- X
- X /*
- X * Now allocate blocks to hold data, read in data, and setup initial
- X * pointers to point to the file headers.
- X */
- X for (i = numrecs; i > 0; i = i - BLOCKSIZE) {
- X
- X bsize = (i > BLOCKSIZE ? BLOCKSIZE : i);
- X block = mymalloc(bsize * UDSIZE);
- X
- X chkabort();
- X if (Read(infile, block, bsize * UDSIZE) != (bsize * UDSIZE)) {
- X print3("BBSindex: Error reading from file",filename,"\n");
- X Cleanup(10);
- X }
- X
- X /*
- X * Now initialise all the pointers for this block. Also
- X * null-terminate the catalogue filename, since BBS-PC!
- X * doesn't always save out the null termination byte.
- X * Also set Online and Valid files to 0, by default.
- X * These will be updated by CHECKFILES.
- X */
- X
- X for (j = 0; j < bsize; j++) {
- X block->cat_name[15] = '\0';
- X block->online = 0;
- X block->valid = 0;
- X block->dirnum = 0;
- X *p++ = block++;
- X }
- X }
- X Close(infile); infile = NULL;
- X}
- X
- X/*
- X * readconfigfile()
- X * ----------------
- X * This function reads in the configuration file (i.e. CONFIG.DAT),
- X * if present, and initialises the directory array with the
- X * directory names. If the file isn't present, readconfig is set
- X * to FALSE, so that CHECKFILES won't work unless directories are
- X * specified on the command line.
- X */
- Xvoid readconfigfile()
- X{
- X int i;
- X long size;
- X
- X size = openfile(configname);
- X if (size != CFGSIZE) {
- X print3("BBSindex: ", configname,
- X " is not a valid configuration file\n");
- X Cleanup(10);
- X }
- X if (Read(infile, config, CFGSIZE) != CFGSIZE) {
- X print3("BBSindex: Error reading from configuration file ",
- X configname, "\n");
- X Cleanup(10);
- X }
- X Close(infile);
- X infile = NULL;
- X for (i = 0; i < NUM_SECT; i++)
- X strcpy(dirnames[i], config->ud_alt[i]);
- X}
- X
- X
- X/*
- X * readscript()
- X * ------------
- X * This function reads in the specified script file into memory.
- X * It allocates memory for the script file on the fly - this
- X * memory should be released by the caller, either through calling
- X * Cleanup(), or explicitly by calling FreeMem(). If the latter,
- X * remember to set script = NULL immediately afterwards.
- X *
- X * The script pointer is set to the start of the script buffer by
- X * this call, so readcommand() will start at the beginning of the file.
- X */
- X
- Xvoid readscript(filename)
- Xchar *filename;
- X{
- X strcpy(scriptname, filename);
- X scriptsize = openfile(filename);
- X if (scriptsize == 0) {
- X print3("BBSindex: Script file ",filename," is empty.\n");
- X Cleanup(10);
- X }
- X
- X script = SafeAllocMem(scriptsize);
- X if (Read(infile, script, scriptsize) != scriptsize) {
- X print3("BBSindex: Error reading script file ", filename, "\n");
- X Cleanup(10);
- X }
- X Close(infile); infile = NULL;
- X scriptpos = 0;
- X linenum = 1;
- X}
- X
- X/*
- X * help()
- X * ------
- X * Prints out a help screen for the program.
- X */
- Xvoid help()
- X{
- X print("BBSindex V1.0 file utility for BBS-PC! "
- X "Copyright (C) Eddy Carroll 1989.\n");
- X print("Usage: bbsindex {options} scriptfile ..\n\n");
- X print("Possible options are:\n\n");
- X print(" -c filename Read configuration from filename (default ");
- X print2(configname, ")\n");
- X print(" -d filename Read database from filename (default ");
- X print2(databasename, ")\n");
- X print(" -f \"string\" Format output as string (default \""
- X "%15n %w %-6x-%b{B,T} %c\")\n");
- X print(" -h Print this help screen\n");
- X print(" -n Disable AmigaDos requesters\n");
- X print(" -t Turn on trace mode\n\n");
- X print("(See documentation for a description of the script language.)\n");
- X Cleanup(5);
- X}
- X
- X/*
- X * doscript()
- X * ----------
- X * Reads in and executes the specified script file.
- X */
- Xvoid doscript(name)
- Xchar *name;
- X{
- X readscript(name);
- X execscript();
- X flushout();
- X FreeMem(script, scriptsize);
- X script = NULL;
- X}
- X
- X
- X/*
- X * Mainline
- X * --------
- X */
- X
- Xvoid main(argc,argv)
- Xint argc;
- Xchar *argv[];
- X{
- X struct Process *me = (struct Process *)FindTask(0L);
- X requeststat = me->pr_WindowPtr;
- X
- X /*
- X * Setup defaults for command line etc.
- X */
- X
- X errorfile = Open("*", MODE_NEWFILE);
- X outfile = Output();
- X toscreen = IsInteractive(outfile);
- X strcpy(formatstring, FORMAT);
- X strcpy(databasename, UDNAME);
- X strcpy(configname, CFGNAME);
- X tree[0].field = E_ALL; /* By default, select all files */
- X
- X
- X /*
- X * Open standard resources
- X */
- X if ((IntuitionBase = OpenLibrary("intuition.library",33)) == NULL) {
- X print("BBSindex: Couldn't open intuition.library, sigh.\n");
- X Cleanup(5);
- X }
- X
- X /*
- X * Allocate space for reading directory info into
- X */
- X fib = mymalloc(sizeof(struct FileInfoBlock));
- X
- X
- X /*
- X * Check to see were we run with the name PROGSCRIPT. Only
- X * check the last part of the filename, in case we were run with
- X * a full path (like BBS:BBSCRIPT for example).
- X */
- X if (!stricmp(argv[0] + strlen(argv[0]) - strlen(PROGSCRIPT), PROGSCRIPT)) {
- X doscript(DEFSCRIPT);
- X Cleanup(0);
- X }
- X
- X /*
- X * Now parse the command line.
- X */
- X if (argc < 2) {
- X help();
- X Cleanup(0);
- X }
- X
- X while (argc > 1) {
- X if (argv[1][0] == '-') {
- X switch (argv[1][1]) {
- X
- X case 'c': /* Set config filename */
- X argv++, argc--;
- X strcpy(configname, argv[1]);
- X break;
- X
- X case 'd': /* Set database filename */
- X argv++; argc--;
- X strcpy(databasename, argv[1]);
- X break;
- X
- X case 'f': /* Format string */
- X argv++; argc--;
- X strcpy(formatstring, argv[1]);
- X strcat(formatstring, "\n");
- X break;
- X
- X case 'h': /* Display help */
- X help();
- X break;
- X
- X case 'n': /* Disable AmigaDos requesters */
- X com_norequest();
- X break;
- X
- X case 't':
- X tracemode = TRUE;
- X break;
- X
- X default:
- X print3("BBSindex: Unknown option ", argv[1], "ignored\n");
- X break;
- X }
- X } else /* Not a command line option, so invoke script */
- X doscript(argv[1]);
- X argv++, argc--;
- X }
- X Cleanup(0);
- X}
- X
- X
- Xvoid __fpinit(){}
- Xvoid __fpterm(){}
- Xvoid MemCleanup(){}
- END_OF_FILE
- if test 13333 -ne `wc -c <'src/bbsindex.c'`; then
- echo shar: \"'src/bbsindex.c'\" unpacked with wrong size!
- fi
- # end of 'src/bbsindex.c'
- fi
- if test -f 'src/command.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'src/command.c'\"
- else
- echo shar: Extracting \"'src/command.c'\" \(22726 characters\)
- sed "s/^X//" >'src/command.c' <<'END_OF_FILE'
- X/*
- X * COMMANDS.C
- X *
- X * This module contains all the command parsing stuff used to parse the
- X * commands in the script file.
- X *
- X */
- X
- X#ifndef LATTICE_50
- X#include "system.h"
- X#endif
- X
- X#include "bbsindex.h"
- X
- X/*
- X * The list of command names and associated functions
- X */
- X
- Xvoid com_append(), com_close(), com_config(), com_database(), com_echo(),
- X com_exec(), com_format(), com_list(), com_macro(), com_msg(),
- X com_open(), com_reset(), com_scan(), com_trace(), com_ignore();
- X
- Xstruct {
- X char *name;
- X void (*proc)();
- X} com[] = {
- X
- X { "APPEND", com_append },
- X { "CHECKFILES", com_checkfiles },
- X { "CLOSE", com_close },
- X { "CONFIG", com_config },
- X { "DATABASE", com_database },
- X { "ECHO", com_echo },
- X { "EXEC", com_exec },
- X { "FOREIGN", com_foreign },
- X { "FORMAT", com_format },
- X { "IGNORE", com_ignore },
- X { "LIST", com_list },
- X { "MACRO", com_macro },
- X { "MSG", com_msg },
- X { "NOREQUEST", com_norequest },
- X { "OPEN", com_open },
- X { "RESET", com_reset },
- X { "SCAN", com_scan },
- X { "SELECT", com_select },
- X { "SORT", com_sort },
- X { "TRACE", com_trace },
- X { NULL, NULL }
- X};
- X
- Xstruct constval {
- X struct constval *next;
- X char *name;
- X char *value;
- X};
- Xtypedef struct constval CONST;
- X
- XCONST *firstconst = NULL; /* Pointer to first constant on list */
- X
- Xstatic char line[MAXCOM]; /* Temporary line buffer */
- X
- X/*
- X * scripterror()
- X * -------------
- X * Prints an error message for the current script command; the filename
- X * and linenumber are automatically output, followed by the user
- X * specified message. The message should be terminated with a NL,
- X * unless the caller intends outputting any more info afterwards.
- X */
- Xvoid scripterror(s)
- Xchar *s;
- X{
- X print3("===> ", scriptname, " (");
- X print3(itoa(linenum), "): ",s);
- X}
- X
- X/*
- X * addconst()
- X * --------
- X * Creates a new constant entry, and links it into the list of
- X * existing constant definitions. The name of the constant is
- X * initialised appropriately. A pointer to the constant is returned.
- X */
- XCONST *addconst(name)
- Xchar *name;
- X{
- X CONST *tv;
- X
- X tv = mymalloc(sizeof(CONST));
- X tv->next = firstconst;
- X firstconst = tv;
- X tv->name = mymalloc(strlen(name)+1);
- X strcpy(tv->name, name);
- X return (tv);
- X}
- X
- X/*
- X * findconst()
- X * ---------
- X * Searches constant table for specified constant, and returns a
- X * pointer to it if found, or NULL if not found.
- X */
- XCONST *findconst(name)
- Xchar *name;
- X{
- X CONST *tv;
- X
- X for (tv = firstconst; tv && stricmp(name, tv->name); tv = tv->next)
- X ;
- X return (tv);
- X}
- X
- X
- X/*
- X * readcommand()
- X * -------------
- X * This function reads a command from the script buffer into a
- X * specified command buffer. The following modifications are made
- X * to the original script text:
- X *
- X * - Anything after a # to an end of line is ignored
- X * - Any line ending with \ is continued on the next line
- X * - Everything outside quotes is capitalised.
- X * - Any white space outside quotes gets reduced to a single space
- X * - White space surrounding command lines is removed.
- X * - Any commas on the line are removed, and replaced by spaces
- X *
- X * The script command is terminated by either a semicolon or a newline.
- X * The command buffer is null-terminated on return. When the end of the
- X * script is reached, a 0 is returned. The maximum size of the command
- X * line is specified in max - if this is exceeded, then an error message
- X * is generated. Normally, the length of the command line read in is
- X * returned.
- X *
- X * For the technically minded, a mini state machine is setup, to handle
- X * the different requirements.
- X */
- X
- X#define STATE_START 1 /* Skip over space at start of command */
- X#define STATE_SPACE 2 /* Replace multiple white space by single space */
- X#define STATE_COPY 3 /* Copy normal text, capitalising */
- X#define STATE_IGNORE 4 /* If next char is newline, then skip it */
- X#define STATE_QUOTE 5 /* Copy text up until next quote */
- X#define STATE_IGQUOTE 6 /* Like STATE_IGNORE, but between quotes */
- X#define STATE_COMMENT 7 /* Ignore everything until the next newline */
- X#define STATE_CONST_ST 8 /* Starting to expand a constant $(..) */
- X#define STATE_CONST_CP 9 /* Copying constant name, and expanding it */
- X
- X/*
- X * Retrieve next character from script buffer, updating line counter
- X * and checking for end of buffer.
- X */
- X#define nextchar(ch) \
- X ( (ch) = script[scriptpos++], \
- X ((ch) == CHAR_NL ? linenum++ : 0), \
- X ((scriptpos > scriptsize) ? (loop = 0) : 0) \
- X )
- X
- X/*
- X * Add character to command line, checking for end of buffer and
- X * aborting if the buffer is overrun.
- X */
- X#define addchar(ch) \
- X ( buf[pos++] = (ch), \
- X ((pos > max) ? (scripterror("line too long\n"), Cleanup(10)) : 0) \
- X )
- X
- Xint readcommand(buf,max)
- Xchar *buf;
- Xint max;
- X{
- X int pos = 0;
- X int state = STATE_START;
- X int laststate;
- X int loop = 1;
- X int ch;
- X CONST *var;
- X char varname[MAXCONST], *p;
- X char openquote;
- X int varpos;
- X
- X if (scriptpos >= scriptsize)
- X return 0;
- X
- X nextchar(ch);
- X while (loop) {
- X switch (state) {
- X
- X case STATE_START:
- X switch (ch) {
- X
- X case CHAR_SPACE:
- X case CHAR_TAB:
- X case CHAR_SEMI:
- X case CHAR_NL:
- X case CHAR_COMMA:
- X nextchar(ch);
- X break;
- X
- X case CHAR_HASH:
- X state = STATE_COMMENT;
- X break;
- X
- X default:
- X state = STATE_COPY;
- X break;
- X }
- X break;
- X
- X case STATE_SPACE:
- X switch (ch) {
- X
- X case CHAR_SPACE:
- X case CHAR_TAB:
- X case CHAR_COMMA:
- X nextchar(ch);
- X break;
- X
- X case CHAR_NL:
- X linenum--;
- X /* Drop through */
- X case CHAR_SEMI:
- X scriptpos--;
- X loop = 0;
- X break;
- X
- X default:
- X addchar(CHAR_SPACE);
- X state = STATE_COPY;
- X break;
- X }
- X break;
- X
- X case STATE_COPY:
- X switch (ch) {
- X
- X /*
- X * If we get a # after a command, then we immediately
- X * stop, so that the comment will get eaten the NEXT
- X * time we call readcommand(). If we went into
- X * STATE_COMMENT, then the following command would end
- X * up getting read into the buffer as well.
- X *
- X */
- X case CHAR_HASH:
- X scriptpos--;
- X loop = 0;
- X break;
- X
- X case CHAR_QUOTE:
- X case CHAR_QUOTES:
- X addchar(ch);
- X openquote = ch;
- X state = STATE_QUOTE;
- X nextchar(ch);
- X break;
- X
- X case CHAR_DOLLAR:
- X laststate = state; /* Save return state */
- X state = STATE_CONST_ST;
- X nextchar(ch);
- X break;
- X
- X case CHAR_NL:
- X linenum--;
- X /* Drop through */
- X
- X case CHAR_SEMI:
- X loop = 0;
- X scriptpos--;
- X break;
- X
- X case CHAR_ESC:
- X nextchar(ch);
- X state = STATE_IGNORE;
- X break;
- X
- X case CHAR_SPACE:
- X case CHAR_TAB:
- X case CHAR_COMMA:
- X state = STATE_SPACE;
- X break;
- X
- X default:
- X addchar(toupper(ch));
- X nextchar(ch);
- X break;
- X }
- X break;
- X
- X case STATE_IGNORE:
- X if (ch == CHAR_NL)
- X ch = CHAR_SPACE;
- X addchar(ch);
- X nextchar(ch);
- X state = STATE_COPY;
- X break;
- X
- X case STATE_QUOTE:
- X switch (ch) {
- X
- X case CHAR_NL:
- X scriptpos--;
- X linenum--;
- X loop = 0;
- X break;
- X
- X case CHAR_ESC:
- X state = STATE_IGQUOTE;
- X nextchar(ch);
- X break;
- X
- X case CHAR_DOLLAR:
- X laststate = state;
- X state = STATE_CONST_ST;
- X nextchar(ch);
- X break;
- X
- X case CHAR_QUOTE:
- X case CHAR_QUOTES:
- X if (openquote == ch)
- X state = STATE_COPY;
- X /* Deliberate fall through to next switch */
- X
- X default:
- X addchar(ch);
- X nextchar(ch);
- X break;
- X }
- X break;
- X
- X case STATE_IGQUOTE:
- X if (ch != CHAR_NL) {
- X addchar(CHAR_ESC);
- X addchar(ch);
- X }
- X nextchar(ch);
- X state = STATE_QUOTE;
- X break;
- X
- X case STATE_COMMENT:
- X if (ch == CHAR_NL)
- X state = STATE_START;
- X nextchar(ch);
- X break;
- X
- X case STATE_CONST_ST:
- X if (ch != '(') { /* If not a constant usage, copy unchanged */
- X addchar(CHAR_DOLLAR);
- X state = laststate;
- X break;
- X }
- X varpos = 0;
- X nextchar(ch);
- X state = STATE_CONST_CP;
- X break;
- X
- X case STATE_CONST_CP:
- X if (ch == ')') {
- X /*
- X * Variable name has been fully entered, so now
- X * expand it.
- X */
- X varname[varpos] = CHAR_NULL;
- X var = findconst(varname);
- X if (!var) {
- X scripterror("unknown constant ");
- X print2(varname, ".\n");
- X Cleanup(10);
- X }
- X for (p = var->value; *p; p++)
- X addchar(*p);
- X state = laststate;
- X } else {
- X /*
- X * Else still gathering constant name, so keep
- X * copying into array.
- X */
- X if (varpos >= MAXCONST) {
- X scripterror("constant name too long.\n");
- X Cleanup(10);
- X }
- X varname[varpos++] = ch;
- X }
- X nextchar(ch);
- X break;
- X }
- X }
- X addchar(CHAR_NULL); /* Null terminate command string */
- X compos = 0;
- X comlen = strlen(buf);
- X return (comlen);
- X}
- X
- X/*
- X * findmacro()
- X * -----------
- X * Searches the macro table for the specified macro. If found, returns
- X * pointer to the macro definition, else returns -1.
- X */
- Xint findmacro(name)
- Xchar *name;
- X{
- X int i;
- X
- X for (i = 0; i < nummacros; i++)
- X if (!strcmp(macros[i]->name, name))
- X return (i);
- X return (-1);
- X}
- X
- X/*
- X * getstring()
- X * -----------
- X * This function scans the command buffer starting at position
- X * 'compos', and returns a pointer to the next identifier/string in
- X * the buffer. The string is null-terminated, and if it was enclosed
- X * in quotes, these are removed. compos is left pointing to just after
- X * the string.
- X */
- Xchar *getstring()
- X{
- X char *s = combuf + compos;
- X char *p, openquote;
- X
- X if (compos > comlen) {
- X scripterror("missing parameter\n");
- X Cleanup(10);
- X }
- X if (*s == CHAR_SPACE)
- X s++;
- X
- X if (*s == CHAR_QUOTE || *s == CHAR_QUOTES) {
- X openquote = *s++;
- X for (p = s; *p && *p != openquote; p++) {
- X if (*p == CHAR_ESC)
- X p++; /* Skip over possible escaped quotes */
- X }
- X *p = CHAR_NULL;
- X compos = (p - combuf) + 1;
- X return (s);
- X }
- X
- X for (p = s; *p && *p != CHAR_SPACE; p++)
- X ;
- X *p = CHAR_NULL;
- X compos = (p - combuf) + 1;
- X return (s);
- X}
- X
- X
- X/*
- X * execline()
- X * ----------
- X * This function executes the current line in the command buffer,
- X * handling macro expansion as necessary.
- X */
- Xvoid execline()
- X{
- X char *cmd;
- X char *p, *s;
- X char *equals;
- X MACRO *curmac;
- X CONST *var;
- X int macronum;
- X char *ps[10];
- X PARAM *par;
- X int length;
- X int i;
- X
- X chkabort();
- X if (tracemode) {
- X if (nestlevel > 0) {
- X int i;
- X for (i = 0; i < nestlevel; i++)
- X print(" ");
- X print2(combuf, "\n");
- X } else {
- X print3(itoa(linenum), ":", scriptname);
- X print3(": ", combuf, "\n");
- X }
- X }
- X cmd = getstring();
- X for (i = 0; com[i].name && strcmp(cmd, com[i].name); i++)
- X ;
- X if (com[i].name) {
- X /*
- X * A valid command was found, so execute it
- X */
- X com[i].proc();
- X } else if ((macronum = findmacro(cmd)) == -1) {
- X /*
- X * Not a macro -- is it a constant definition?
- X */
- X if (compos < comlen) {
- X /*
- X * There's a parameter after it; check is it an equals sign
- X */
- X equals = getstring();
- X if (equals[0] != CHAR_EQUALS || equals[1] != CHAR_NULL) {
- X /*
- X * Wasn't an equals, so probably just a wrong command
- X * with some superfluous parameters.
- X */
- X scripterror("unknown command ");
- X print2(cmd, "\n");
- X Cleanup(10);
- X }
- X /*
- X * Okay, it's a constant definition. If it's already been
- X * defined, print a warning but carry on anyway.
- X */
- X var = findconst(cmd);
- X if (var) {
- X scripterror("constant ");
- X print2(cmd, " redefined.\n");
- X } else
- X var = addconst(cmd);
- X /*
- X * Now see was a value specified for the constant.
- X * If it was, copy it into constant, else just
- X * setup a null definition.
- X */
- X if (compos < comlen) {
- X /*
- X * Copy user's definition
- X */
- X cmd = getstring();
- X if (strlen(cmd) >= MAXCONST) {
- X scripterror("constant name too long.\n");
- X Cleanup(10);
- X }
- X var->value = mymalloc(strlen(cmd)+1);
- X strcpy(var->value, cmd);
- X } else {
- X /*
- X * Setup null constant definition
- X */
- X var->value = mymalloc(1);
- X strcpy(var->value, "");
- X }
- X } else {
- X /*
- X * Not a command, not a macro, not a constant definition.
- X * Therefore, must be an error.
- X */
- X scripterror("unknown command ");
- X print2(cmd, "\n");
- X Cleanup(10);
- X }
- X } else {
- X /*
- X * It's a macro. Save a copy of the macro parameters into
- X * $0 to $9, and then execute each line of the macro definition,
- X * expanding parameter usages as necessary.
- X */
- X curmac = macros[macronum];
- X length = 0;
- X if (nestlevel >= MAXNEST) {
- X scripterror("macros can only be nested ");
- X print2(MAXNEST, " deep\n");
- X Cleanup(10);
- X }
- X par = SafeAllocMem(PARAMSIZE + comlen + 1);
- X params[nestlevel++] = par;
- X par->size = comlen + 1;
- X
- X /*
- X * Now initialise parameters $0 to $9
- X */
- X ps[0] = par->params;
- X for (i = 1; i < 10; i++) {
- X if (compos < comlen)
- X /* Save parameter for later */
- X ps[i] = par->params + (getstring() - combuf);
- X else
- X ps[i] = NULL;
- X }
- X memcpy(par->params, combuf, comlen+1);
- X
- X /*
- X * Now recursively call execline() with command buffer setup
- X * to be the current macro buffer line.
- X */
- X length = 0;
- X s = curmac->text;
- X for (length = 0; length < curmac->size;
- X length += strlen(s) + 1, s = curmac->text + length) {
- X /* Copy macro line into buffer, doing expansion */
- X comlen = 0;
- X for (p = s; *p; p++) {
- X if (*p == '$') {
- X p++;
- X if (!*p) {
- X scripterror("'$' must be followed by something\n");
- X Cleanup(10);
- X }
- X if (isdigit(*p)) {
- X int num = *p - '0';
- X
- X if (ps[num]) {
- X /* Copy macro definition */
- X if ((strlen(ps[num]) + comlen) >= MAXCOM - 10)
- X goto endcommand; /* Eek! my first C Goto! */
- X strcpy(combuf+comlen, ps[num]);
- X comlen += strlen(ps[num]);
- X }
- X continue;
- X
- X }
- X }
- X combuf[comlen++] = *p;
- X }
- Xendcommand:
- X combuf[comlen] = CHAR_NULL;
- X compos = 0;
- X execline();
- X }
- X /*
- X * Now free memory allocated for macro parameters
- X */
- X FreeMem(par, PARAMSIZE + par->size);
- X nestlevel--;
- X }
- X}
- X
- X
- X/*
- X * execscript()
- X * ------------
- X * This function goes through all the commands in the script file,
- X * executing them one by one. Each command is parsed, and then
- X * the appropriate function called. Any errors that occur cause the
- X * script to be aborted, so if this call returns, the script was
- X * successfully executed.
- X */
- Xvoid execscript()
- X{
- X while (readcommand(combuf, MAXCOM))
- X execline();
- X}
- X
- X/*
- X * ====> Now the actual commands follow <====
- X */
- X
- X/*
- X * com_append()
- X * ------------
- X * This file opens the specified file for appending. If there was
- X * already a file open, it is closed.
- X */
- X
- Xvoid com_append()
- X{
- X char *filename = getstring();
- X BPTR lock;
- X
- X com_close();
- X
- X if (lock = Lock(filename, ACCESS_READ)) {
- X UnLock(lock);
- X outfile = Open(filename, MODE_OLDFILE);
- X } else
- X outfile = Open(filename, MODE_NEWFILE);
- X
- X if (!outfile) {
- X scripterror("error appending to file ");
- X print2(filename, "\n");
- X Cleanup(10);
- X }
- X Seek(outfile, 0, OFFSET_END);
- X toscreen = IsInteractive(outfile);
- X}
- X
- X/*
- X * com_open()
- X * ----------
- X * This command opens a file for output. The output of the ECHO,
- X * FOREIGN and LIST commands goes to this file. If an output file
- X * was already open, it is closed first.
- X */
- Xvoid com_open()
- X{
- X char *filename = getstring();
- X
- X com_close();
- X outfile = Open(filename, MODE_NEWFILE);
- X if (!outfile) {
- X scripterror("error opening file ");
- X print2(filename, "\n");
- X Cleanup(10);
- X }
- X toscreen = IsInteractive(outfile);
- X}
- X
- X/*
- X * com_close()
- X * -----------
- X * Closes the current output file, if any. All future output goes to
- X * stdout.
- X */
- Xvoid com_close()
- X{
- X if (outfile != Output()) {
- X flushout();
- X Close(outfile);
- X outfile = Output();
- X toscreen = IsInteractive(outfile);
- X }
- X}
- X
- X/*
- X * com_config()
- X * ------------
- X * This command sets the name of the configuration file used by
- X * CHECKFILES to get the names of the directories to scan from.
- X */
- Xvoid com_config()
- X{
- X strcpy(configname, getstring());
- X}
- X
- X/*
- X * com_database()
- X * --------------
- X * This command sets the name of the database file read in
- X * which contains the details of all the file headers.
- X */
- Xvoid com_database()
- X{
- X strcpy(databasename, getstring());
- X}
- X
- X/*
- X * com_norequest()
- X * ---------------
- X * This command stops AmigaDos from putting up requesters when an
- X * error occurs, or a volume isn't mounted.
- X */
- Xvoid com_norequest()
- X{
- X struct Process *me = (struct Process *)FindTask(0L);
- X me->pr_WindowPtr = (APTR)-1L;
- X}
- X
- X/*
- X * com_echo()
- X * ----------
- X * This command echoes the specified string to the output file.
- X * A number of meta characters may be present in the string to be
- X * output. See the documentation for more info. If any other parameter
- X * is present on the line after the string, no newline is added
- X * to the output, otherwise a newline is added automatically.
- X *
- X */
- Xvoid com_echo()
- X{
- X strcpy(line, getstring());
- X if (compos >= comlen) /* Add NL if no second parameter */
- X strcat(line, "\n");
- X putstring(echoformat(out, MAXOUT, line));
- X}
- X
- X/*
- X * com_exec()
- X * ----------
- X * This command executes the indicated AmigaDOS command. The output
- X * from the command goes into the current output file, if any.
- X */
- Xvoid com_exec()
- X{
- X flushout();
- X Execute(getstring(), NULL, outfile);
- X}
- X
- X/*
- X * com_format()
- X * ------------
- X * This command sets up the format string used by LIST and FOREIGN.
- X * For details of what the format may contain, see the documentation.
- X */
- Xvoid com_format()
- X{
- X strcpy(formatstring, getstring());
- X if (compos >= comlen) /* No second parameter */
- X strcat(formatstring, "\n");
- X}
- X
- X/*
- X * com_list()
- X * ----------
- X * This command is the biggy! It scans through the entire file
- X * database, selecting records as specified with SELECT, and printing
- X * them using the order specified with FORMAT. The order of printing
- X * is as specified with SORT.
- X */
- Xvoid com_list()
- X{
- X int i;
- X
- X CHECKDATABASE();
- X curbytes = 0;
- X curfiles = 0;
- X for (i = 0; i < numrecs; i++) {
- X if (ptrblock[i]->type == 0 && match(ptrblock[i], tree)) {
- X format(out, MAXOUT, formatstring, ptrblock[i], checkfiles);
- X putstring(out);
- X curfiles++;
- X curbytes += ptrblock[i]->length;
- X }
- X chkabort();
- X }
- X totalbytes += curbytes;
- X totalfiles += curfiles;
- X}
- X
- X/*
- X * com_msg()
- X * ---------
- X * This command is identical to the ECHO command, except that
- X * the output goes to the screen (i.e. stderr) instead of the
- X * current output file. It is intended for printing informational
- X * messages to let the user know of the program's progress, while
- X * processing a large script.
- X */
- Xvoid com_msg()
- X{
- X strcpy(line, getstring());
- X if (compos >= comlen) /* Add NL if no second parameter */
- X strcat(line, "\n");
- X print(echoformat(out, MAXOUT, line));
- X}
- X
- X/*
- X * com_reset()
- X * -----------
- X * This command resets the running totals which are maintained,
- X * which give the total number of bytes and total number of files
- X * output so far.
- X */
- Xvoid com_reset()
- X{
- X totalfiles = 0;
- X totalbytes = 0;
- X}
- X
- X/*
- X * com_scan()
- X * ----------
- X * This command is identical to com_list(), except that no output is
- X * produced. It is provided to allow counters to be updated, without
- X * producing output, so that for example, the total number of files
- X * in a listing can be output before the files themselves.
- X */
- X
- Xvoid com_scan()
- X{
- X int i;
- X
- X CHECKDATABASE();
- X curbytes = 0;
- X curfiles = 0;
- X for (i = 0; i < numrecs; i++) {
- X if (ptrblock[i]->type == 0 && match(ptrblock[i], tree)) {
- X curfiles++;
- X curbytes += ptrblock[i]->length;
- X }
- X chkabort();
- X }
- X totalbytes += curbytes;
- X totalfiles += curfiles;
- X}
- X
- X/*
- X * com_macro()
- X * -----------
- X * This command lets you define a macro. The syntax is:
- X *
- X * MACRO name
- X * ..
- X * commands
- X * ..
- X * ENDM
- X *
- X * The macro can contain $1 to $9, which will be substituted at
- X * run time by the appropriate parameters that were passed when
- X * the macro was invoked.
- X */
- Xvoid com_macro()
- X{
- X static char macroname[MACROLEN];
- X int curline, curpos; /* Saves current line # and position in script */
- X int length; /* Length of macro script */
- X int macronum;
- X MACRO *curmac;
- X char *buf, *p;
- X
- X if (nummacros >= MAXMACRO) {
- X scripterror("maximum of ");
- X print2(itoa(MAXMACRO), " macros allowed\n");
- X Cleanup(10);
- X }
- X
- X curline = linenum;
- X curpos = scriptpos;
- X
- X p = getstring();
- X strncpy(macroname, p, MACROLEN-1);
- X macroname[MACROLEN-1] = CHAR_NULL;
- X
- X /*
- X * First of all, find out how much space the macro occupies
- X */
- X length = 0;
- X while (readcommand(combuf, MAXCOM) && strcmp(combuf, "ENDM"))
- X length += comlen + 1; /* The extra 1 is for the terminating \0 */
- X
- X /*
- X * Now, restore linenumber and script position so we can read
- X * in macro for real the second time.
- X */
- X linenum = curline;
- X if (scriptpos >= scriptsize) {
- X scripterror("missing ENDM in macro definition\n");
- X Cleanup(10);
- X }
- X
- X scriptpos = curpos;
- X
- X if ((macronum = findmacro(macroname)) == -1) /* New macro */
- X macronum = nummacros++;
- X else
- X /* Macro redefinition, so release earlier definition */
- X FreeMem(macros[macronum], macros[macronum]->size + MACROSIZE);
- X
- X curmac = SafeAllocMem(MACROSIZE + length);
- X macros[macronum] = curmac;
- X strcpy(curmac->name, macroname);
- X curmac->size = length;
- X buf = curmac->text;
- X
- X /*
- X * Initialised macro definition, now copy macro text from script
- X * file into macro buffer.
- X */
- X while (readcommand(combuf, MAXCOM) && strcmp("ENDM", combuf)) {
- X strcpy(buf, combuf);
- X buf += comlen + 1;
- X }
- X}
- X
- X/*
- X * com_trace()
- X * -----------
- X * Turns on or off trace mode. When trace mode is on, every line that
- X * is executed is displayed first. This is handy when executing
- X * macros, to see exactly what is happening. Note that the -t option
- X * on the command line will enable tracing for the whole file,
- X * but if TRACE ON or OFF is encountered, this overrides -t.
- X */
- Xvoid com_trace()
- X{
- X char *opt = getstring();
- X
- X if (!strcmp(opt, "ON"))
- X tracemode = TRUE;
- X else if (!strcmp(opt, "OFF"))
- X tracemode = FALSE;
- X else {
- X scripterror("TRACE ON or TRACE OFF expected\n");
- X Cleanup(10);
- X }
- X}
- X
- X/*
- X * com_ignore()
- X * ------------
- X * This command takes a list of filenames. Each filename listed here
- X * is remembered, and when CHECKFILES is done, any filename listed
- X * here will be marked as valid, regardless of whether it really IS
- X * valid or not. This is useful if you have some files online which
- X * are updated by external programs (such as, for example, the output
- X * from BBSINFO), and hence have "wandering" file sizes.
- X */
- Xvoid com_ignore()
- X{
- X IGNORE *ig;
- X char *p;
- X
- X while (compos < comlen) {
- X ig = mymalloc(sizeof(IGNORE));
- X p = getstring();
- X if (strlen(p) >= CAT_LEN) {
- X scripterror("filename too long.\n");
- X Cleanup(10);
- X }
- X strcpy(ig->name, p);
- X ig->next = firstignore;
- X firstignore = ig;
- X }
- X}
- END_OF_FILE
- if test 22726 -ne `wc -c <'src/command.c'`; then
- echo shar: \"'src/command.c'\" unpacked with wrong size!
- fi
- # end of 'src/command.c'
- fi
- if test -f 'src/format.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'src/format.c'\"
- else
- echo shar: Extracting \"'src/format.c'\" \(18069 characters\)
- sed "s/^X//" >'src/format.c' <<'END_OF_FILE'
- X/*
- X * FORMAT.C
- X *
- X * This is a general purpose format routine for BBSINDEX. It takes
- X * as parameters a format string and a pointer to a file header and creates
- X * a string, based on the format string, but containing information from
- X * the file header.
- X *
- X * The format string is similar to a printf format string. The following
- X * special sequences are translated into values associated with the
- X * fileheader.
- X *
- X * %a Number of accesses
- X * %b{} File type {Binary,Text}
- X * %c Comment
- X * %d Disk filename
- X * %f Full name of disk file
- X * %i{} File status {Online,Offline}
- X * %k File size in K
- X * %l{} File origin {Local,Remote}
- X * %n Filename
- X * %o Uploader's name (owner)
- X * %p Path name of disk file
- X * %r Directory number
- X * %s Section number or letter
- X * %ux Output x n times, where n is length of last {sub}format cmd
- X * %v{} File contents {Valid,Invalid}
- X * %w Upload date (when)
- X * %x File size
- X * %y Disk directory number
- X * %{...} Format substring in { }
- X *
- X * The following escape sequences are also recognised:
- X *
- X * \n - End of line
- X * \e - Escape
- X * \E - CSI (0x9b)
- X * \t - Tab
- X * \nnn - Octal number with the appropriate value
- X *
- X * Any other characters after a \ are treated as normal. This can be used
- X * to escape certain characters that have special meaning, such as \ itself,
- X * %, and sometimes {, }, and comma.
- X *
- X * With the % flags, additional characters may appear between the % and the
- X * value. Using %c as an example, %20c means make the comment 20 characters
- X * wide, padding with spaces as necessary. The comment is aligned to the
- X * left. %-20c is similar, but aligns the comment to the right. This can
- X * be useful for padding. Items followed by {} are boolean flags, which
- X * can be either true or false. Inside the {} are two items, seperated by
- X * a comma. If the flag is true, the first value is used, else the second
- X * value. The expressions inside {} may not include %.
- X *
- X * The exception to this is %m, which can contain a complete format
- X * string within the {}. In this case, the substring is formatted as
- X * normal, and then the resulting string is formatted according to
- X * the parameters between the % and m. For example %10m{(%s,%d)} formats
- X * the section and directory number in brackets, while ensuring that
- X * the total field width is 10 characters.
- X *
- X * n.b. A file is said to be valid if its actual filesize is equivalent
- X * to the filesize in the file catalogue.
- X *
- X */
- X
- X#ifndef LATTICE_50
- X#include "system.h"
- X#endif
- X
- X#include "bbsindex.h"
- X
- X#define LEFT 0 /* Left alignment */
- X#define RIGHT 1 /* Right alignment */
- X#define MAXPATH 256 /* Maximum length of disk path */
- X
- X#define MIN(a,b) ((a) > (b) ? (b) : (a))
- X
- X#define LEFTBR '{' /* Left bracket */
- X#define RIGHTBR '}' /* Right bracket */
- X#define BACKSLASH '\\' /* Backslash */
- X
- Xstatic int len; /* Length of format option */
- Xstatic int align; /* Text alignment - LEFT or RIGHT */
- Xstatic int opos; /* Position in output string */
- Xstatic int omaxlen; /* Maximum length of output string */
- X
- Xstatic char buf[256];
- X
- Xstatic char *sectnums[] = {
- X "0", "1", "2", "3", "4", "5", "6", "7",
- X "8", "9", "A", "B", "C", "D", "E", "F"
- X};
- X
- Xstatic char *days[] = {
- X "Sunday", "Monday", "Tuesday", "Wednesday",
- X "Thursday", "Friday", "Saturday"
- X};
- X
- X/*
- X * doescape()
- X * ----------
- X * This function parses the escape sequence passed as a string,
- X * and stores the character it corresponds to in the output character.
- X * it returns the number of characters taken up by the escape sequence.
- X */
- Xint doescape(out, seq)
- Xunsigned char *out;
- Xunsigned char *seq;
- X{
- X int num = 0;
- X char *p;
- X
- X if (*seq >= '0' && *seq <= '7') {
- X p = seq;
- X for (p = seq; *p >= '0' && *p <= '7'; p++)
- X num = (num * 8) + (*seq - '0');
- X *out = num;
- X return (p-seq);
- X }
- X switch (*seq) {
- X
- X case 't':
- X *out = '\t';
- X break;
- X
- X case 'n':
- X *out = '\n';
- X break;
- X
- X case 'e':
- X *out = '\033';
- X break;
- X
- X case 'E':
- X *out = '\233';
- X break;
- X
- X default:
- X *out = *seq;
- X break;
- X }
- X return (1);
- X}
- X
- X
- X/*
- X * makedate()
- X * ----------
- X * This function takes the passed day, month and year and returns
- X * a pointer to a string containing dd-mmm-yy (e.g. 23-Mar-89).
- X */
- X
- X#define ITOA(c1,c2,x) ((c1) = ((x)/10 + '0'),(c2) = (((x) % 10) + '0'))
- X
- Xchar *makedate(day, month, year)
- Xint day, month, year;
- X{
- X static char buf[10];
- X
- X ITOA(buf[0], buf[1], day);
- X buf[3] = months[month][0];
- X buf[4] = months[month][1];
- X buf[5] = months[month][2];
- X ITOA(buf[7], buf[8], year);
- X buf[2] = '-';
- X buf[6] = '-';
- X buf[9] = CHAR_NULL;
- X return (buf);
- X}
- X
- X/*
- X * maketime()
- X * ----------
- X * Returns a pointer to a string containing the specified time, in
- X * the form HH:MM:SS.
- X */
- Xchar *maketime(secs, mins, hours)
- Xint secs, mins, hours;
- X{
- X static char buf[9];
- X ITOA(buf[0], buf[1], hours);
- X ITOA(buf[3], buf[4], mins);
- X ITOA(buf[6], buf[7], secs);
- X buf[2] = ':';
- X buf[5] = ':';
- X buf[8] = CHAR_NULL;
- X return (buf);
- X}
- X
- X/*
- X * itoa()
- X * ------
- X * This function returns a pointer to a string containing the ascii
- X * representation of the number. The pointer is valid until the next
- X * time itoa() is called. Negative numbers are not handled.
- X */
- Xchar *itoa(n)
- Xint n;
- X{
- X static char buf[20];
- X int i = 18;
- X
- X buf[19] = CHAR_NULL;
- X if (n == 0) {
- X buf[18] = '0';
- X return (buf + 18);
- X } else {
- X for ( ; n && (i > 0); i--) {
- X buf[i] = (n % 10) + '0';
- X n = n / 10;
- X }
- X }
- X return (buf+i+1);
- X}
- X
- X
- X/*
- X * addstring()
- X * -----------
- X * This functions adds string 's' to string 'out' starting at position
- X * (global) opos. If (global) len == 0, then no special formatting is
- X * done. Else, the string is formatted in a field of width 'len'
- X * characters, truncating or padding with spaces as appropriate. If
- X * (global) align == LEFT, then the string is aligned to the left
- X * of the field, else to the right.
- X *
- X * If opos exceeds omaxlen at any point, then no more copying is done.
- X */
- X
- Xvoid addstring(out, s)
- Xchar *out;
- Xchar *s;
- X{
- X int oleft = omaxlen - opos; /* Number of chars left in output string */
- X int slen = strlen(s);
- X
- X /* Note: Be VERY careful the following hasn't got changed into tabs! */
- X static char pad[] = "\
- X \
- X \
- X ";
- X /* End of space definition! There should be ~200 spaces */
- X
- X if (len == 0) { /* No special alignment needed */
- X if (oleft > 0)
- X strncpy(out+opos,s,MIN(slen,oleft));
- X opos = opos + slen;
- X } else { /* Do special alignment */
- X if (slen > len)
- X strncpy(out+opos, s, len);
- X else {
- X if (align == LEFT) {
- X if (oleft > 0)
- X /* Copy string into left of field */
- X strncpy(out+opos, s, MIN(slen,oleft));
- X oleft = oleft - slen;
- X if (oleft > 0 && len > slen)
- X /* Copy padding in to rest of field */
- X strncpy(out+opos+slen, pad, MIN(len-slen, oleft));
- X } else { /* Align == RIGHT */
- X if (len > slen)
- X /* Copy padding into left of field */
- X strncpy(out+opos, pad, MIN(oleft, len-slen));
- X if (oleft > 0)
- X /* Copy string into right of field */
- X strncpy(out+opos+(len-slen), s, MIN(slen,oleft-slen));
- X }
- X }
- X opos = opos + len;
- X }
- X}
- X
- X/*
- X * addnumber()
- X * -----------
- X * This macro adds the ascii representation of the number n
- X * to the output buffer 'out', starting at position opos (global).
- X * See addstring() for more details.
- X */
- X
- X#define addnumber(out,n) addstring((out),itoa(n))
- X
- X/*
- X * format()
- X * --------
- X * This function converts the format string f and the file header into
- X * an output string. See above for valid options in the format string.
- X * A pointer to the output string is returned. Maxlen is the maximum
- X * length of the output string. Checkfiles is a boolean which is true
- X * if the dirnum, online and valid fields in the file header are valid.
- X * These are normally NOT valid, unless requested by the user.
- X *
- X * The external variable dirnames is expected to exist. Dirnames is
- X * an array of disk dirctories (NOT BBS file directories).
- X */
- X
- Xchar *format(out, maxlen, f, fhead, checkfiles)
- Xchar *out;
- Xint maxlen;
- Xchar *f;
- XUDHEAD *fhead;
- Xint checkfiles;
- X{
- X char subformat[MAXSUB];
- X static int lastlen; /* Length of last {sub}format string */
- X int fpos = 0; /* Position in format string */
- X int flen = strlen(f);
- X int bool, numchars;
- X char *p, *s, *rightbr;
- X
- X omaxlen = maxlen-1; /* Make this global for convenience */
- X opos = 0;
- X
- X for (fpos = 0; fpos < flen && opos < maxlen; fpos++) {
- X if (f[fpos] == BACKSLASH) {
- X fpos += doescape(out+opos, f+fpos+1);
- X opos++;
- X } else if (f[fpos] != '%')
- X out[opos++] = f[fpos];
- X else {
- X align = LEFT;
- X if (f[++fpos] == '-') {
- X align = RIGHT;
- X fpos++;
- X }
- X len = 0;
- X while (isdigit(f[fpos]))
- X len = (len * 10) + f[fpos++] - '0';
- X
- X switch (f[fpos]) {
- X
- X case 'a': /* Number of file accesses */
- X addnumber(out,fhead->accesses);
- X break;
- X
- X case 'c': /* File comment */
- X addstring(out,fhead->desc);
- X break;
- X
- X case 'd':
- X addstring(out,fhead->disk_name);
- X break;
- X
- X case 'f':
- X if (checkfiles) {
- X *buf = CHAR_NULL;
- X if (fhead->online) {
- X char ch;
- X strcat(buf, dirnames[fhead->dirnum]);
- X ch = buf[strlen(buf)-1];
- X if (ch != '/' && ch != ':')
- X strcat(buf, "/");
- X }
- X strcat(buf,fhead->disk_name);
- X addstring(out,buf);
- X } else
- X addstring(out,fhead->disk_name); /* Maintain formatting */
- X break;
- X
- X /*
- X * Note that all the boolean-related options are grouped
- X * together, so that they can have common error handling.
- X * A bit nasty perhaps, but it works :-)
- X *
- X * Note that 'i' and 'v' default to being online and
- X * valid respectively, if checkfiles hasn't been
- X * selected.
- X */
- X
- X case 'b': /* True if file is binary */
- X case 'i': /* True if file is online */
- X case 'l': /* True if file uploaded locally */
- X case 'v': /* True if file is valid */
- X
- X switch (f[fpos]) {
- X case 'b': bool = fhead->bin;
- X break;
- X case 'i': bool = checkfiles ? fhead->online : 0;
- X break;
- X case 'l': bool = fhead->local;
- X break;
- X case 'v': bool = checkfiles ? fhead->valid : 0;
- X break;
- X }
- X /* Make sure { and } surround arguments */
- X rightbr = strchr(f+fpos, RIGHTBR);
- X if (f[fpos+1] == LEFTBR && rightbr != NULL) {
- X p = f + (fpos+2);
- X s = buf;
- X
- X if (bool) { /* Use first argument */
- X /* Copy until comma reached */
- X /* Handle any escaped characters (\n, \t etc) */
- X while (*p && *p != ',' && *p != RIGHTBR) {
- X if (*p == BACKSLASH) {
- X p = p + doescape(s++, p+1) + 1;
- X } else
- X *s++ = *p++;
- X }
- X } else {
- X /* As above, but copy second argument */
- X p = strchr(p,',');
- X if (p) {
- X p++;
- X while (*p && *p != RIGHTBR) {
- X if (*p == BACKSLASH) {
- X p = p + doescape(s++, p+1) + 1;
- X } else
- X *s++ = *p++;
- X }
- X }
- X }
- X *s = CHAR_NULL;
- X addstring(out,buf);
- X /* Skip over braces */
- X fpos = (rightbr - f);
- X }
- X break;
- X
- X case 'k': /* File size in K */
- X addnumber(out, BTOK(fhead->length));
- X break;
- X
- X case 'n': /* Catalogue file name of file */
- X addstring(out, fhead->cat_name);
- X break;
- X
- X case 'o': /* Owner of upload */
- X addstring(out, fhead->owner);
- X break;
- X
- X case 'p': /* Path name to disk file */
- X if (checkfiles && fhead->online)
- X addstring(out, dirnames[fhead->dirnum]);
- X break;
- X
- X case 'r': /* Directory number */
- X addnumber(out, fhead->dir);
- X break;
- X
- X case 's': /* Section names */
- X addstring(out, sectnums[fhead->section]);
- X break;
- X
- X case 'u': /* Underline with next char */
- X fpos++;
- X numchars = lastlen + ((align == RIGHT) ? -len : len);
- X while (numchars > 0 && opos < omaxlen) {
- X out[opos++] = f[fpos];
- X numchars--;
- X }
- X break;
- X
- X case 'w': /* Date */
- X addstring(out,
- X makedate( (fhead->date & 31), /* Day */
- X ((fhead->date>>5)) % 13, /* Month */
- X ((fhead->date>>5)) / 13)); /* Year */
- X break;
- X
- X case 'x':
- X addnumber(out, fhead->length);
- X break;
- X
- X case 'y':
- X addnumber(out, fhead->dirnum);
- X break;
- X
- X case '{': /* Format substring in { } */
- X {
- X int numbrackets = 0;
- X int savepos, savealign, savelen;
- X p = &f[fpos + 1];
- X /*
- X * Now, find the end of the substring. Nested
- X * brackets are skipped over.
- X */
- X while (*p && !(*p == RIGHTBR && numbrackets == 0)) {
- X switch (*p) {
- X case LEFTBR: numbrackets++; break;
- X case RIGHTBR: numbrackets--; break;
- X }
- X p++;
- X }
- X if (*p == RIGHTBR) {
- X /*
- X * To format the substring, we change the
- X * closing } into a NULL so that when
- X * we call format recursively, it will
- X * think that's the end of the string.
- X * After format()ing, we restore the
- X * bracket.
- X */
- X savepos = opos;
- X savelen = len;
- X savealign = align;
- X *p = CHAR_NULL;
- X /* Format substring */
- X format(subformat, MAXSUB, &f[fpos + 1],
- X fhead, checkfiles);
- X *p = RIGHTBR;
- X opos = savepos;
- X len = savelen;
- X align = savealign;
- X addstring(out, subformat);
- X fpos = p - f;
- X }
- X }
- X break;
- X
- X default:
- X len = 0;
- X buf[0] = '%';
- X buf[1] = f[fpos];
- X buf[2] = CHAR_NULL;
- X addstring(out, buf);
- X }
- X }
- X }
- X out[opos] = CHAR_NULL;
- X lastlen = opos;
- X return (out);
- X}
- X
- X/*
- X * echoformat()
- X * ------------
- X * This function takes a format string (as passed to the ECHO command)
- X * and uses it to produce an output string. The format string can
- X * contain the same escape sequences list under format(), and also
- X * the following special sequences:
- X *
- X * %b - Number of bytes occupied by files in last LIST command
- X * %k - Number of kilobytes occupied by files in last LIST command
- X * %m - Number of megabytes occupied by files in last LIST command
- X * %n - Number of files in last LIST command
- X *
- X * %B, %K, %M and %N are similar to the above except that they
- X * represent the running totals for all files listed since the
- X * last RESET command.
- X *
- X * %w - The current date, in dd-mmm-yy format.
- X * %d - The current day (Monday, Tuesday etc.)
- X * %t - The current time (24 hour clock)
- X * %u - As for format()
- X * %{..} - As for format()
- X */
- Xchar *echoformat(out, maxlen, f)
- Xchar *out;
- Xint maxlen;
- Xchar *f;
- X{
- X static int lastlen;
- X char subformat[MAXSUB];
- X int fpos = 0; /* Position in format string */
- X int flen = strlen(f);
- X int value;
- X int numchars;
- X struct tm *today;
- X long seconds;
- X char *p;
- X
- X /*
- X * First of all, setup the time array
- X */
- X time(&seconds);
- X today = localtime(&seconds);
- X
- X /*
- X * Now format output string, according to format string
- X */
- X omaxlen = maxlen-1;
- X opos = 0;
- X
- X for (fpos = 0; fpos < flen && opos < maxlen; fpos++) {
- X if (f[fpos] == BACKSLASH) {
- X fpos += doescape(out+opos, f+fpos+1);
- X opos++;
- X } else if (f[fpos] != '%')
- X out[opos++] = f[fpos];
- X else {
- X align = LEFT;
- X if (f[++fpos] == '-') {
- X align = RIGHT;
- X fpos++;
- X }
- X len = 0;
- X while (isdigit(f[fpos]))
- X len = (len * 10) + f[fpos++] - '0';
- X
- X value = -1;
- X switch (f[fpos]) {
- X
- X case 'b': value = curbytes; break;
- X case 'B': value = totalbytes; break;
- X case 'k': value = BTOK(curbytes); break;
- X case 'K': value = BTOK(totalbytes); break;
- X case 'n': value = curfiles; break;
- X case 'N': value = totalfiles; break;
- X case 'm': value = BTOK(BTOK(curbytes)); break;
- X case 'M': value = BTOK(BTOK(totalbytes)); break;
- X
- X case 'd': /* Today's day */
- X addstring(out, days[today->tm_wday]);
- X break;
- X case 'w': /* Today's date */
- X addstring(out, makedate(
- X today->tm_mday, /* Day */
- X today->tm_mon+1, /* Month */
- X today->tm_year )); /* Year */
- X break;
- X
- X case 't': /* Today's time */
- X addstring(out,
- X maketime(today->tm_sec, today->tm_min, today->tm_hour));
- X break;
- X
- X case 'u': /* Underline with next char */
- X fpos++;
- X numchars = lastlen + ((align == RIGHT) ? -len : len);
- X while (numchars > 0 && opos < omaxlen) {
- X out[opos++] = f[fpos];
- X numchars--;
- X }
- X break;
- X
- X /*
- X * Note - I'm a little unhappy about including this code
- X * twice, but a nice simple way of generalising it for
- X * both format and echoformat doesn't spring to mind.
- X */
- X case '{': /* Format substring in { } */
- X {
- X int numbrackets = 0;
- X int savepos, savealign, savelen;
- X p = &f[fpos + 1];
- X /*
- X * Now, find the end of the substring. Nested
- X * brackets are skipped over.
- X */
- X while (*p && !(*p == RIGHTBR && numbrackets == 0)) {
- X switch (*p) {
- X case LEFTBR: numbrackets++; break;
- X case RIGHTBR: numbrackets--; break;
- X }
- X p++;
- X }
- X if (*p == RIGHTBR) {
- X /*
- X * To format the substring, we change the
- X * closing } into a NULL so that when
- X * we call format recursively, it will
- X * think that's the end of the string.
- X * After format()ing, we restore the
- X * bracket.
- X */
- X savepos = opos;
- X savelen = len;
- X savealign = align;
- X *p = CHAR_NULL;
- X /* Format substring */
- X echoformat(subformat, MAXSUB, &f[fpos + 1]);
- X *p = RIGHTBR;
- X opos = savepos;
- X len = savelen;
- X align = savealign;
- X addstring(out, subformat);
- X fpos = p - f;
- X }
- X }
- X break;
- X
- X default:
- X len = 0;
- X buf[0] = '%';
- X buf[1] = f[fpos];
- X buf[2] = CHAR_NULL;
- X addstring(out, buf);
- X }
- X if (value != -1)
- X addnumber(out, value);
- X }
- X }
- X out[opos] = CHAR_NULL;
- X lastlen = opos;
- X return (out);
- X}
- END_OF_FILE
- if test 18069 -ne `wc -c <'src/format.c'`; then
- echo shar: \"'src/format.c'\" unpacked with wrong size!
- fi
- # end of 'src/format.c'
- fi
- echo shar: End of archive 2 \(of 3\).
- cp /dev/null ark2isdone
- MISSING=""
- for I in 1 2 3 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 3 archives.
- rm -f ark[1-9]isdone
- else
- echo You still need to unpack the following archives:
- echo " " ${MISSING}
- fi
- ## End of shell archive.
- exit 0
- --
- Submissions to comp.sources.amiga and comp.binaries.amiga should be sent to:
- amiga@cs.odu.edu
- or amiga@xanth.cs.odu.edu ( obsolescent mailers may need this address )
- or ...!uunet!xanth!amiga ( very obsolescent mailers need this address )
-
- Comments, questions, and suggestions s should be addressed to ``amiga-request''
- (only use ``amiga'' for submissions) at the above addresses.
-